#include "LBPExtractor.h"

LBPExtractor::LBPExtractor(int overlapping, int nBlocksX, int nBlocksY):
m_overlapping(overlapping),
m_nBlocksX(nBlocksX),
m_nBlocksY(nBlocksY),
m_pTmpImage(NULL),
m_pBorderImage(NULL),
m_piPatternCodec(NULL),
m_blockIDTable(NULL)
{
	m_nBlocksTotal = nBlocksX * nBlocksY;
}

LBPExtractor::~LBPExtractor(void)
{
	if (NULL != m_pTmpImage)
	{
		cvReleaseMat(&m_pTmpImage);
		cvReleaseMat(&m_pBorderImage);
	}
	if (NULL != m_piPatternCodec)
	{
		delete[] m_piPatternCodec;
		delete[] m_blockIDTable;
	}
}

int LBPExtractor::Extract( CvMat* pGaborImage, BYTE* pSubBuf )
{
	if (NULL == m_pTmpImage)
	{
		m_pTmpImage = cvCreateMat(pGaborImage->rows, pGaborImage->cols, CV_32S);
		m_pBorderImage = cvCreateMat(pGaborImage->rows + 2, pGaborImage->cols + 2, CV_32S);
		Prepare();
	}
	else if (this->m_pTmpImage->rows != pGaborImage->rows || m_pTmpImage->cols != pGaborImage->cols)
	{
		cvReleaseMat(&m_pTmpImage);
		cvReleaseMat(&m_pBorderImage);
		m_pTmpImage = cvCreateMat(pGaborImage->rows, pGaborImage->cols, CV_32S);
		m_pBorderImage = cvCreateMat(pGaborImage->rows + 2, pGaborImage->cols + 2, CV_32S);
		Prepare();
	}
	LBPTransform( pGaborImage);
	LBPHistogram(pSubBuf);

	return 0;
}

void LBPExtractor::LBPTransform(CvMat* pGaborImage)
{
	float* ptrGabor = (float*)cvPtr2D(pGaborImage, 0,0);
	int* ptrTmp = (int*)cvPtr2D(m_pTmpImage,0,0);
	int totalPoints = pGaborImage->rows * pGaborImage->cols;
	for (int i=0; i< totalPoints; i++)
	{
		*ptrTmp++ = static_cast<int>(*ptrGabor++);
	}

	cvCopyMakeBorder(m_pTmpImage, m_pBorderImage, cvPoint(1,1), IPL_BORDER_CONSTANT);
	int r = pGaborImage->rows + 1;
	int c = pGaborImage->cols + 1;
	int* ptrBase = (int*)cvPtr2D(m_pBorderImage,1,1); //CV_32S
	int* ptrResult = (int*)cvPtr2D(m_pTmpImage,0,0); //CV_32S
	int width = m_pBorderImage->width;
	for (int i=1; i<r; i++)
	{
		int* center = ptrBase;
		for (int j=1; j<c;j++)
		{
			unsigned int code = 0;
			int* p0 = center + width;
			int* p1 = center - width;
			compab_mask(*(p1 + 1),		0);
			compab_mask(*(center + 1),	1);
			compab_mask(*(p0 + 1),		2);
			compab_mask(*(p0),			3);
			compab_mask(*(p0 - 1),		4);
			compab_mask(*(center - 1),	5);
			compab_mask(*(p1 - 1),		6);
			compab_mask(*(p1),			7);
			center++;
			*ptrResult++ = code;
		}
		ptrBase += width;
	}
}

void LBPExtractor::LBPHistogram(BYTE* pSubBuf)
{
	int length = nBinNum*m_nBlocksTotal;
	memset(pSubBuf, 0, length);
	int* ptrResult = (int*)cvPtr2D(m_pTmpImage,0,0);
	int totalPoints = m_pTmpImage->rows * m_pTmpImage->cols;
	int pos = 0;
	for (int i=0; i<totalPoints; i++)
	{
		pos = m_blockIDTable[i] + m_piPatternCodec[*ptrResult++];
		assert(pos < length);
		pSubBuf[pos]++;
	}
}

void LBPExtractor::Prepare(void)
{
	int totalPoints = m_pTmpImage->rows * m_pTmpImage->cols;
	int blockHeight = m_pTmpImage->rows % m_nBlocksY == 0 ? m_pTmpImage->rows / m_nBlocksY : m_pTmpImage->rows / m_nBlocksY + 1;
	int blockWidth = m_pTmpImage->cols % m_nBlocksX ==0 ? m_pTmpImage->cols / m_nBlocksX : m_pTmpImage->cols / m_nBlocksX + 1;
	int width = m_pTmpImage->cols;

	if (NULL != m_blockIDTable)
	{
		delete m_blockIDTable;
	}
	m_blockIDTable = new int[totalPoints];
	int *tmp = m_blockIDTable;
	for (int i=0; i<totalPoints; i++)
	{
		*tmp++ = (i/width/blockHeight*m_nBlocksX + (i%width)/blockWidth)*nBinNum;
	}

	if (NULL != m_piPatternCodec)
	{
		delete m_piPatternCodec;
	}
	m_piPatternCodec = new int[256];
	int iCode = 0;
	for (int i=0; i<256;i++)
	{
		if (GetUniformCount(i,8)<=2)
		{
			m_piPatternCodec[i] =  iCode++;
		}
		else
		{
			m_piPatternCodec[i] =  nBinNum - 1;
		}
	}
}

int LBPExtractor::GetUniformCount(int code, int maxBit) 
{
	int count = 0;
	code |= ((code & 1) << maxBit);
	for (int i = 0; i < maxBit; i ++) {
		if ( (code & 1) != ((code & 2) >> 1) )
			count ++;
		code >>= 1;
	}
	return count;
}